﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using nwrole.lib;

namespace nwrole.lib2 {
    public static class DeployUtil {
        #region Services

        public static string ServiceIdentifierFormat = "nwrole.integration.services.hosted.{0}";

        public static bool InstallService(ServiceData serviceData, int supervisorPort) {
            if (ServiceInstalled(serviceData.Id))
                return true;
            EventLogger.LogInformation("DeployUtil.InstallService", String.Format("Installing service '{0}'.", serviceData.Id));
            /*serviceData.ManagementPort = 0;
            serviceData.ManagementPort = NextFreeManagementPort();*/
            ServiceUtil.InstallService(Path.Combine(Configuration.RootDirectoryPath, Configuration.PluginServiceExe) + " " + serviceData.Id.ToString("N") + " " + serviceData.Name + " "+ serviceData.ManagementPort.ToString() + " " + supervisorPort,
                ServiceIdentifier(serviceData.Id),
                serviceData.Name,
                serviceData.Username,
                serviceData.Password);
            EventLogger.LogInformation("DeployUtil.InstallService", String.Format("Installed service '{0}'.", serviceData.Id));
            return true;
        }

        public static bool UninstallService(Guid serviceId) {
            if (!ServiceInstalled(serviceId))
                return true;
            EventLogger.LogInformation("DeployUtil.UninstallService", String.Format("Uninstalling service '{0}'.", serviceId));
            ServiceUtil.UninstallService(ServiceIdentifier(serviceId));
            EventLogger.LogInformation("DeployUtil.UninstallService", String.Format("Uninstalled service '{0}'.", serviceId));
            return true;
        }

        public static bool StartService(Guid serviceId) {
            if (!ServiceInstalled(serviceId) || ServiceRunning(serviceId))
                return false;
            ServiceController ctrl = new ServiceController(ServiceIdentifier(serviceId));
            try {
                ctrl.Start();
                ctrl.WaitForStatus(ServiceControllerStatus.Running);
            } catch (Exception exception) {
                EventLogger.LogException("DeployUtil.StartService", exception);
                return false;
            } finally { ctrl.Dispose(); }
            return true;
        }

        public static bool StopService(Guid serviceId) {
            if (!ServiceInstalled(serviceId) || !ServiceRunning(serviceId))
                return false;
            ServiceController ctrl = new ServiceController(ServiceIdentifier(serviceId));
            try {
                ctrl.Stop();
                ctrl.WaitForStatus(ServiceControllerStatus.Stopped);
            } catch (Exception exception) {
                EventLogger.LogException("DeployUtil.StopService", exception);
                return false;
            } finally { ctrl.Dispose(); }
            return true;
        }

        public static bool ServiceInstalled(Guid serviceId) {
            ServiceController ctrl = new ServiceController(ServiceIdentifier(serviceId));
            try { var status = ctrl.Status; } catch (Exception exception) {
                return false;
            } finally { ctrl.Dispose(); }
            return true;
        }

        public static bool ServiceRunning(Guid serviceId) {
            if (!ServiceInstalled(serviceId))
                return false;
            ServiceController ctrl = new ServiceController(ServiceIdentifier(serviceId));
            try { return (ctrl.Status == ServiceControllerStatus.Running); } catch (Exception exception) {
                EventLogger.LogException("DeployUtil.ServiceInstalled", exception);
                return false;
            } finally { ctrl.Dispose(); }
        }

        /*public static bool ServiceManagerRunning(nwrole.lib.Solution.Service service) {
            ServiceManagement mgr = null;
            try {
                mgr = new ServiceManagement(service);
                if (mgr.GetIdentifier().ToString("N") != service.Id.ToString("N"))
                    return false;
            } catch (Exception exception) {
                EventLogger.LogException("DeployUtil.ServiceManagerRunning", exception);
                return false;
            } finally {
                if (mgr != null)
                    mgr.Dispose();
            }
            return true;
        }*/

        public static string ServiceIdentifier(Guid id) {
            return String.Format(ServiceIdentifierFormat, id.ToString("N"));
        }

        #endregion
        #region Solutions
        public static Result DeployService(ServiceData serviceData, bool forceDeployment, int supervisorPort) {
            //ServiceData serviceData = Configuration.ServiceDataWithId(serviceId);
            
            if (serviceData == null) {
                EventLogger.LogError("DeployUtil.DeployService", String.Format("Service '{0}' does not exist.", serviceData));
                return Result.SolutionIdUnavailable;
            }

            if (!forceDeployment && serviceData.Deployed) {
                EventLogger.LogError("DeployUtil.DeployService", String.Format("Service '{0}' is already deployed", serviceData));
                return Result.SolutionAlreadyDeployed;
            }

            if (DeployUtil.ServiceInstalled(serviceData.Id)) {
                if (DeployUtil.ServiceRunning(serviceData.Id))
                    DeployUtil.StopService(serviceData.Id);
                DeployUtil.UninstallService(serviceData.Id);
            }

            if (DeployUtil.InstallService(serviceData, supervisorPort)) {
                serviceData.ServiceState = ServiceState.Activated;
                serviceData.DeployedDate = DateTime.Now;
                serviceData.Deployed = true;

                if (DeployUtil.StartService(serviceData.Id)) {
                    serviceData.ServiceState = ServiceState.Running;

                    /*ServiceChannel channel = new ServiceChannel(serviceData);
                    channel.Start();
                    channel.Dispose();*/
                }
            }
            
            //Configuration.Save();

            return Result.Success;
        }

        public static Result RetractService(ServiceData serviceData, bool forceRetraction) {
            //ServiceData serviceData = Configuration.ServiceDataWithId(serviceId);

            /*if (serviceData == null) {
                EventLogger.LogError("DeployUtil.RetractService", String.Format("Service '{0}' does not exist.", serviceId));
                return Result.SolutionIdUnavailable;
            }*/

            if (!serviceData.Deployed && !forceRetraction) {
                EventLogger.LogError("DeployUtil.RetractService", String.Format("Service '{0}' is already retracted", serviceData.Id));
                return Result.SolutionAlreadyRetracted;
            }

            if (serviceData.ServiceState == ServiceState.Running) {
                DeployUtil.StopService(serviceData.Id);
                serviceData.ServiceState = ServiceState.Activated;
            }
            if (serviceData.ServiceState != ServiceState.Deactivated) {
                DeployUtil.UninstallService(serviceData.Id);
                serviceData.ServiceState = ServiceState.Deactivated;
            }

            serviceData.Deployed = false;
            serviceData.DeployedDate = null;

            //Configuration.Save();

            return Result.Success;
        }
        #endregion
    }
}
